iT邦幫忙

2025 iThome 鐵人賽

DAY 21
1
DevOps

GitLab CI 2025:深入玩轉流水線與實戰紀錄系列 第 21

Day21 - 只在特定 Git Branch 的 Git Tag 執行 GitLab CI Job

  • 分享至 

  • xImage
  •  

今天的題目來自 Stack Overflow 「How to run a Gitlab-CI job only if a tag is created on the main branch」如何僅讓建立在特定分支的 Git Tag,可以建立 GitLab CI Job ?

  rules:
    - if: $CI_COMMIT_TAG && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  script: echo "do something"

提問者列了嘗試過的 GitLab CI/CD YAML,並描述上面的題目。

目前使用的方法有什麼問題?

在原提問者的 stack overflow 上提到他目前嘗試過的 CI/CD YAML,為什麼這個 .gitlab-ci.yml 無效呢? 在判斷是上可以理解,原提問者想表達的邏輯是,當有下 Tag,而且是下在 CI_DEFAULT_BRANCH 預設分支的 Tag,才執行這個 Job。那,為什麼不會執行呢?

在 GitLab CI/CD variables reference 的頁面中有提到 CI_COMMIT_BRANCH 這個 Pre-Defined 的變數,在由 merge request 或 tag 所觸發的 Pipeline 中,是沒有作用的。

The commit branch name. Available in branch pipelines, including pipelines for the default branch. Not available in merge request pipelines or tag pipelines.

所以 rules 中的 if,在透過 Git Tag 觸發 Pipeline CI_COMMIT_TAG 有數值時,依據目前的 GitLab 預設的行為 CI_COMMIT_BRANCH 這個變數,預設是不存在,所以判斷條件 $CI_COMMIT_TAG && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH 永遠不會成立,因此無法執行。

同樣的,當不是透過 Git 下 Tag 觸發 Pipeline,而是一般 Push 更新 Git Branch,則 CI_COMMIT_TAG 這個變數也是預設不存在。

The commit tag name. Available only in pipelines for tags.

這個問題是合理的嗎?

只有在下在特定分支的 Tag,才執行特定的 Job。就個人的實務經驗,目前覺得不是太合理,因為,一般而言,在 Git 上建立 Tag,通常是為了紀錄特定的事件,可能是版本的釋出、或特定的里程碑完成,而這些事件,通常需要觸發的 GitLab Pipeline 內容,應該要是一致的,當需要判斷該 Tag 是否在特定的分支上,則可能需要再次思考,建立 Tag 的程序、什麼使用者可以建立 Tag,這些程序是否有問題?

如果是在特定形式的 Tags 建立時,才執行特定工作,則會更合理一些。例如透過 GitLab 的 Protected tags 功能,限定特定形式的 Tag,僅能由有特定權限的使用者來建立,這時候就僅需要在 GitLab CI/CD 的 rules 判斷式上,加強驗證 CI_COMMIT_TAG 的形式是否符合,例如 Tag 名稱 v 開頭的 CI_COMMIT_TAG ,才能執行對應的 Job 。

run_on_tag:
  rules:
    - if: $CI_COMMIT_TAG =~ "/^v.*/"
  script:
    - echo "run on v tag"

實驗一:在 Script 中透過 Git 指令反查目前 Git Sha 所在的 Branch 來作為判斷

既然是題目,先不管是否合理,如果真的需要知道目前 Git Tag 所在的 Branch 可以怎麼辦到?
這邊主要透過 git ls-remote --heads origin 查詢 Git 目前所在位置的 SHA,是否為 main ,再後續的 if 判斷來決定是否執行後續 Script。

# file name: .gitlab-ci.yml

always_run:
  script:
    - echo "CI_COMMIT_TAG = $CI_COMMIT_TAG"
    - echo "CI_COMMIT_BRANCH = $CI_COMMIT_BRANCH"
    - echo "CI_COMMIT_SHORT_SHA = $CI_COMMIT_SHORT_SHA"
    - echo "CI_COMMIT_REF_NAME = $CI_COMMIT_REF_NAME"
    - git --version

run_on_tag:
  rules:
    - if: $CI_COMMIT_TAG =~ "/^v.*/"
  script:
    - echo "run on v tag"

run_on_main_brang_tag:
  rules:
    - if: $CI_COMMIT_TAG
  before_script:
    - TAG_ON_MAIN=$(git ls-remote --heads origin | grep $CI_COMMIT_SHORT_SHA | grep -q main; echo $?)
    - if [[ $TAG_ON_MAIN -ne 0 ]]; then echo 'not main'; exit 0; fi
  script:
    - echo "TAG = $CI_COMMIT_TAG and branch on Main"
    - echo "is on Main"

但,可以想像,這個解法必須透過 Script 內容來進行判斷,不是太容易閱讀的解法。

總結

依據上面的內容,雖然可以透過 Script 使用 Git 指令來判斷目前 Git Tag 所在的 Branch,但因為是在 Script 中,其也增加的 CI/CD YAML 的閱讀難度,因此,如果可以,還是比較推薦透過團隊內部制度來建立合理的 Git Tag,而不是透過判斷 Tag 所在 Branch 來進行判斷。閱讀這篇文章的你,覺得呢? 你們會用什麼方法來滿足這個需求?我是墨嗓(陳佑竹),期待這次的內容能帶給你實用的啟發與幫助。

參考範例

參考連結


上一篇
Day20 - 如何在 GitLab CI/CD YAML 裡像 function 一樣重複使用
下一篇
Day22 - 讓 Pipeline 的所有 Job 都在同一個 Runner 執行 - 1
系列文
GitLab CI 2025:深入玩轉流水線與實戰紀錄23
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言